#pragma once

#include <iostream>
#include <queue>
#include <vector>
#include "lockGuard.hpp"
#include "pthread.hpp"

const static int pthreadNumDefault = 5;

template <class T>
class ThreadPool
{
public:
    // Member functions
    ThreadPool(int num = pthreadNumDefault)
        : _pthreadNum(num)
    {
        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&_cond, nullptr);
    }

    ~ThreadPool()
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_cond);
    }
    // Important ways
    void init()
    {
        for (int i = 0; i < _pthreadNum; ++i)
        {
            _pthreadList.push_back(thread(i, threadRoutine, this));
        }
    }

    void start()
    {
        for (int i = 0; i < _pthreadNum; ++i)
        {
            _pthreadList[i].run();
        }
    }

    void pushTask(const T &task)
    {
        lockGuard lock(&_mutex);
        {

            _taskQueue.push(task);
        }
        threadWakeUp();
    }

    // for debug
    void check()
    {
        for (int i = 0; i < _pthreadNum; ++i)
        {
            std::cout << "tid:" << _pthreadList[i].threadId() << "  name:" << _pthreadList[i].threadName()
                      << "  status:" << _pthreadList[i].threadStatus() << std::endl;
        }
    }

private:
    static void threadRoutine(void *arg)
    {
        pthread_detach(pthread_self());
        ThreadPool<T> *pool = static_cast<ThreadPool<T> *>(arg);
        while (true)
        {
            T task;
            {
                lockGuard lock(pool->getMutex());
                while (pool->isEmpty())
                {
                    pool->threadWait();
                }
                task = pool->popTask();
            }
            task();
            std::cout << "tid:" << pthread_self() << "  thread done, ";
            task.print();
        }
    }

    pthread_mutex_t *getMutex()
    {
        return &_mutex;
    }

    bool isEmpty()
    {
        return _taskQueue.empty();
    }
    void threadWait()
    {
        pthread_cond_wait(&_cond, &_mutex);
    }
    void threadWakeUp()
    {
        pthread_cond_signal(&_cond);
    }
    T popTask()
    {
        T task = _taskQueue.front();
        _taskQueue.pop();
        return task;
    }

private:
    std::vector<thread> _pthreadList;
    int _pthreadNum;
    std::queue<T> _taskQueue;
    pthread_mutex_t _mutex;
    pthread_cond_t _cond;
};